home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 2002 #11 / Amiga Plus CD - 2002 - No. 11.iso / Tools / Development / ncurses-5.3 / ncurses / base / lib_mouse.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-10-27  |  28.3 KB  |  1,015 lines

  1. /****************************************************************************
  2.  * Copyright (c) 1998-2000,2002 Free Software Foundation, Inc.              *
  3.  *                                                                          *
  4.  * Permission is hereby granted, free of charge, to any person obtaining a  *
  5.  * copy of this software and associated documentation files (the            *
  6.  * "Software"), to deal in the Software without restriction, including      *
  7.  * without limitation the rights to use, copy, modify, merge, publish,      *
  8.  * distribute, distribute with modifications, sublicense, and/or sell       *
  9.  * copies of the Software, and to permit persons to whom the Software is    *
  10.  * furnished to do so, subject to the following conditions:                 *
  11.  *                                                                          *
  12.  * The above copyright notice and this permission notice shall be included  *
  13.  * in all copies or substantial portions of the Software.                   *
  14.  *                                                                          *
  15.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
  16.  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
  17.  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
  18.  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
  19.  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
  20.  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
  21.  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
  22.  *                                                                          *
  23.  * Except as contained in this notice, the name(s) of the above copyright   *
  24.  * holders shall not be used in advertising or otherwise to promote the     *
  25.  * sale, use or other dealings in this Software without prior written       *
  26.  * authorization.                                                           *
  27.  ****************************************************************************/
  28.  
  29. /****************************************************************************
  30.  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
  31.  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
  32.  ****************************************************************************/
  33.  
  34. /*
  35.  * This module is intended to encapsulate ncurses's interface to pointing
  36.  * devices.
  37.  *
  38.  * The first method used is xterm's internal mouse-tracking facility.
  39.  * The second is Alessandro Rubini's GPM server.
  40.  *
  41.  * Notes for implementors of new mouse-interface methods:
  42.  *
  43.  * The code is logically split into a lower level that accepts event reports
  44.  * in a device-dependent format and an upper level that parses mouse gestures
  45.  * and filters events.  The mediating data structure is a circular queue of
  46.  * MEVENT structures.
  47.  *
  48.  * Functionally, the lower level's job is to pick up primitive events and
  49.  * put them on the circular queue.  This can happen in one of two ways:
  50.  * either (a) _nc_mouse_event() detects a series of incoming mouse reports
  51.  * and queues them, or (b) code in lib_getch.c detects the kmous prefix in
  52.  * the keyboard input stream and calls _nc_mouse_inline to queue up a series
  53.  * of adjacent mouse reports.
  54.  *
  55.  * In either case, _nc_mouse_parse() should be called after the series is
  56.  * accepted to parse the digested mouse reports (low-level MEVENTs) into
  57.  * a gesture (a high-level or composite MEVENT).
  58.  *
  59.  * Don't be too shy about adding new event types or modifiers, if you can find
  60.  * room for them in the 32-bit mask.  The API is written so that users get
  61.  * feedback on which theoretical event types they won't see when they call
  62.  * mousemask. There's one bit per button (the RESERVED_EVENT bit) not being
  63.  * used yet, and a couple of bits open at the high end.
  64.  */
  65.  
  66. #ifdef __EMX__
  67. #  include <io.h>
  68. #  define  INCL_DOS
  69. #  define  INCL_VIO
  70. #  define  INCL_KBD
  71. #  define  INCL_MOU
  72. #  define  INCL_DOSPROCESS
  73. #  include <os2.h>        /* Need to include before the others */
  74. #endif
  75.  
  76. #include <curses.priv.h>
  77. #include <term.h>
  78.  
  79. #if USE_GPM_SUPPORT
  80. #ifndef LINT            /* don't need this for llib-lncurses */
  81. #undef buttons            /* term.h defines this, and gpm uses it! */
  82. #include <gpm.h>
  83. #include <linux/keyboard.h>    /* defines KG_* macros */
  84. #endif
  85. #endif
  86.  
  87. MODULE_ID("$Id: lib_mouse.c,v 1.61 2002/09/28 16:08:58 tom Exp $")
  88.  
  89. #define MY_TRACE TRACE_ICALLS|TRACE_IEVENT
  90.  
  91. #define INVALID_EVENT    -1
  92.  
  93. static int mousetype;
  94. #define M_XTERM        -1    /* use xterm's mouse tracking? */
  95. #define M_NONE        0    /* no mouse device */
  96. #define M_GPM        1    /* use GPM */
  97. #define M_QNX        2    /* QNX mouse on console */
  98. #define M_QNX_TERM    3    /* QNX mouse on pterm/xterm (using qansi-m) */
  99.  
  100. #if USE_GPM_SUPPORT
  101. #ifndef LINT
  102. static Gpm_Connect gpm_connect;
  103. #endif
  104. #endif
  105.  
  106. static mmask_t eventmask;    /* current event mask */
  107.  
  108. static bool _nc_mouse_parse(int);
  109. static void _nc_mouse_resume(SCREEN *);
  110. static void _nc_mouse_wrap(SCREEN *);
  111.  
  112. /* maintain a circular list of mouse events */
  113.  
  114. /* The definition of the circular list size (EV_MAX), is in curses.priv.h, so
  115.  * wgetch() may refer to the size and call _nc_mouse_parse() before circular
  116.  * list overflow.
  117.  */
  118. static MEVENT events[EV_MAX];    /* hold the last mouse event seen */
  119. static MEVENT *eventp = events;    /* next free slot in event queue */
  120. #define NEXT(ep)    ((ep == events + EV_MAX - 1) ? events : ep + 1)
  121. #define PREV(ep)    ((ep == events) ? events + EV_MAX - 1 : ep - 1)
  122.  
  123. #ifdef TRACE
  124. static void
  125. _trace_slot(const char *tag)
  126. {
  127.     MEVENT *ep;
  128.  
  129.     _tracef(tag);
  130.  
  131.     for (ep = events; ep < events + EV_MAX; ep++)
  132.     _tracef("mouse event queue slot %ld = %s",
  133.         (long) (ep - events),
  134.         _tracemouse(ep));
  135. }
  136. #endif
  137.  
  138. #ifdef USE_EMX_MOUSE
  139.  
  140. #  define TOP_ROW          0
  141. #  define LEFT_COL         0
  142.  
  143. static int mouse_wfd;
  144. static int mouse_thread;
  145. static int mouse_activated;
  146. static char mouse_buttons[] =
  147. {0, 1, 3, 2};
  148.  
  149. #  define M_FD(sp) sp->_mouse_fd
  150.  
  151. static void
  152. write_event(int down, int button, int x, int y)
  153. {
  154.     char buf[6];
  155.     unsigned long ignore;
  156.  
  157.     strncpy(buf, key_mouse, 3);    /* should be "\033[M" */
  158.     buf[3] = ' ' + (button - 1) + (down ? 0 : 0x40);
  159.     buf[4] = ' ' + x - LEFT_COL + 1;
  160.     buf[5] = ' ' + y - TOP_ROW + 1;
  161.     DosWrite(mouse_wfd, buf, 6, &ignore);
  162. }
  163.  
  164. static void
  165. mouse_server(unsigned long ignored GCC_UNUSED)
  166. {
  167.     unsigned short fWait = MOU_WAIT;
  168.     /* NOPTRRECT mourt = { 0,0,24,79 }; */
  169.     MOUEVENTINFO mouev;
  170.     HMOU hmou;
  171.     unsigned short mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN | MOUSE_BN3_DOWN;
  172.     int nbuttons = 3;
  173.     int oldstate = 0;
  174.     char err[80];
  175.     unsigned long rc;
  176.  
  177.     /* open the handle for the mouse */
  178.     if (MouOpen(NULL, &hmou) == 0) {
  179.     rc = MouSetEventMask(&mask, hmou);
  180.     if (rc) {        /* retry with 2 buttons */
  181.         mask = MOUSE_BN1_DOWN | MOUSE_BN2_DOWN;
  182.         rc = MouSetEventMask(&mask, hmou);
  183.         nbuttons = 2;
  184.     }
  185.     if (rc == 0 && MouDrawPtr(hmou) == 0) {
  186.         for (;;) {
  187.         /* sit and wait on the event queue */
  188.         rc = MouReadEventQue(&mouev, &fWait, hmou);
  189.         if (rc) {
  190.             sprintf(err, "Error reading mouse queue, rc=%lu.\r\n", rc);
  191.             break;
  192.         }
  193.         if (!mouse_activated)
  194.             goto finish;
  195.  
  196.         /*
  197.          * OS/2 numbers a 3-button mouse inconsistently from other
  198.          * platforms:
  199.          *      1 = left
  200.          *      2 = right
  201.          *      3 = middle.
  202.          */
  203.         if ((mouev.fs ^ oldstate) & MOUSE_BN1_DOWN)
  204.             write_event(mouev.fs & MOUSE_BN1_DOWN,
  205.                 mouse_buttons[1], mouev.col, mouev.row);
  206.         if ((mouev.fs ^ oldstate) & MOUSE_BN2_DOWN)
  207.             write_event(mouev.fs & MOUSE_BN2_DOWN,
  208.                 mouse_buttons[3], mouev.col, mouev.row);
  209.         if ((mouev.fs ^ oldstate) & MOUSE_BN3_DOWN)
  210.             write_event(mouev.fs & MOUSE_BN3_DOWN,
  211.                 mouse_buttons[2], mouev.col, mouev.row);
  212.  
  213.           finish:
  214.         oldstate = mouev.fs;
  215.         }
  216.     } else
  217.         sprintf(err, "Error setting event mask, buttons=%d, rc=%lu.\r\n",
  218.             nbuttons, rc);
  219.  
  220.     DosWrite(2, err, strlen(err), &rc);
  221.     MouClose(hmou);
  222.     }
  223.     DosExit(EXIT_THREAD, 0L);
  224. }
  225.  
  226. static void
  227. server_state(const int state)
  228. {                /* It would be nice to implement pointer-off and stop looping... */
  229.     mouse_activated = state;
  230. }
  231.  
  232. #endif
  233.  
  234. static int initialized;
  235.  
  236. static void
  237. initialize_mousetype(void)
  238. {
  239.     static const char *xterm_kmous = "\033[M";
  240.  
  241.     /* Try gpm first, because gpm may be configured to run in xterm */
  242. #if USE_GPM_SUPPORT
  243.     /* GPM: initialize connection to gpm server */
  244.     gpm_connect.eventMask = GPM_DOWN | GPM_UP;
  245.     gpm_connect.defaultMask = ~(gpm_connect.eventMask | GPM_HARD);
  246.     gpm_connect.minMod = 0;
  247.     gpm_connect.maxMod = ~((1 << KG_SHIFT) | (1 << KG_SHIFTL) | (1 << KG_SHIFTR));
  248.     if (Gpm_Open(&gpm_connect, 0) >= 0) {    /* returns the file-descriptor */
  249.     mousetype = M_GPM;
  250.     SP->_mouse_fd = gpm_fd;
  251.     return;
  252.     }
  253. #endif
  254.  
  255.     /* OS/2 VIO */
  256. #ifdef USE_EMX_MOUSE
  257.     if (!mouse_thread
  258.     && strstr(cur_term->type.term_names, "xterm") == 0
  259.     && key_mouse) {
  260.     int handles[2];
  261.  
  262.     if (pipe(handles) < 0) {
  263.         perror("mouse pipe error");
  264.         return;
  265.     } else {
  266.         int rc;
  267.  
  268.         if (!mouse_buttons[0]) {
  269.         char *s = getenv("MOUSE_BUTTONS_123");
  270.  
  271.         mouse_buttons[0] = 1;
  272.         if (s && strlen(s) >= 3) {
  273.             mouse_buttons[1] = s[0] - '0';
  274.             mouse_buttons[2] = s[1] - '0';
  275.             mouse_buttons[3] = s[2] - '0';
  276.         }
  277.         }
  278.         mouse_wfd = handles[1];
  279.         M_FD(SP) = handles[0];
  280.         /* Needed? */
  281.         setmode(handles[0], O_BINARY);
  282.         setmode(handles[1], O_BINARY);
  283.         /* Do not use CRT functions, we may single-threaded. */
  284.         rc = DosCreateThread((unsigned long *) &mouse_thread,
  285.                  mouse_server, 0, 0, 8192);
  286.         if (rc) {
  287.         printf("mouse thread error %d=%#x", rc, rc);
  288.         return;
  289.         } else {
  290.         mousetype = M_XTERM;
  291.         return;
  292.         }
  293.     }
  294.     }
  295. #endif
  296.  
  297.     /* we know how to recognize mouse events under "xterm" */
  298.     if (key_mouse != 0) {
  299.     if (!strcmp(key_mouse, xterm_kmous)) {
  300.         mousetype = M_XTERM;
  301.         return;
  302.     }
  303.     } else if (strstr(cur_term->type.term_names, "xterm") != 0) {
  304.     (void) _nc_add_to_try(&(SP->_keytry), xterm_kmous, KEY_MOUSE);
  305.     mousetype = M_XTERM;
  306.     return;
  307.     }
  308. }
  309.  
  310. static void
  311. _nc_mouse_init(void)
  312. /* initialize the mouse */
  313. {
  314.     int i;
  315.  
  316.     if (!initialized) {
  317.     initialized = TRUE;
  318.  
  319.     TR(MY_TRACE, ("_nc_mouse_init() called"));
  320.  
  321.     for (i = 0; i < EV_MAX; i++)
  322.         events[i].id = INVALID_EVENT;
  323.  
  324.     initialize_mousetype();
  325.  
  326.     T(("_nc_mouse_init() set mousetype to %d", mousetype));
  327.     }
  328. }
  329.  
  330. /*
  331.  * Query to see if there is a pending mouse event.  This is called from
  332.  * fifo_push() in lib_getch.c
  333.  */
  334. static bool
  335. _nc_mouse_event(SCREEN * sp GCC_UNUSED)
  336. {
  337. #if USE_GPM_SUPPORT
  338.     /* GPM: query server for event, return TRUE if we find one */
  339.     Gpm_Event ev;
  340.  
  341.     if (Gpm_GetEvent(&ev) == 1) {
  342.     eventp->id = 0;        /* there's only one mouse... */
  343.  
  344.     eventp->bstate = 0;
  345.     switch (ev.type & 0x0f) {
  346.     case (GPM_DOWN):
  347.         if (ev.buttons & GPM_B_LEFT)
  348.         eventp->bstate |= BUTTON1_PRESSED;
  349.         if (ev.buttons & GPM_B_MIDDLE)
  350.         eventp->bstate |= BUTTON2_PRESSED;
  351.         if (ev.buttons & GPM_B_RIGHT)
  352.         eventp->bstate |= BUTTON3_PRESSED;
  353.         break;
  354.     case (GPM_UP):
  355.         if (ev.buttons & GPM_B_LEFT)
  356.         eventp->bstate |= BUTTON1_RELEASED;
  357.         if (ev.buttons & GPM_B_MIDDLE)
  358.         eventp->bstate |= BUTTON2_RELEASED;
  359.         if (ev.buttons & GPM_B_RIGHT)
  360.         eventp->bstate |= BUTTON3_RELEASED;
  361.         break;
  362.     default:
  363.         break;
  364.     }
  365.  
  366.     eventp->x = ev.x - 1;
  367.     eventp->y = ev.y - 1;
  368.     eventp->z = 0;
  369.  
  370.     /* bump the next-free pointer into the circular list */
  371.     eventp = NEXT(eventp);
  372.     return (TRUE);
  373.     }
  374. #endif
  375.  
  376. #ifdef USE_EMX_MOUSE
  377.     {
  378.     char kbuf[3];
  379.  
  380.     int i, res = read(M_FD(sp), &kbuf, 3);    /* Eat the prefix */
  381.     if (res != 3)
  382.         printf("Got %d chars instead of 3 for prefix.\n", res);
  383.     for (i = 0; i < res; i++) {
  384.         if (kbuf[i] != key_mouse[i])
  385.         printf("Got char %d instead of %d for prefix.\n",
  386.                (int) kbuf[i], (int) key_mouse[i]);
  387.     }
  388.     return TRUE;
  389.     }
  390. #endif /* USE_EMX_MOUSE */
  391.  
  392.     /* xterm: never have to query, mouse events are in the keyboard stream */
  393.     return (FALSE);        /* no event waiting */
  394. }
  395.  
  396. static bool
  397. _nc_mouse_inline(SCREEN * sp)
  398. /* mouse report received in the keyboard stream -- parse its info */
  399. {
  400.     TR(MY_TRACE, ("_nc_mouse_inline() called"));
  401.  
  402.     if (mousetype == M_XTERM) {
  403.     unsigned char kbuf[4];
  404.     MEVENT *prev;
  405.     size_t grabbed;
  406.     int res;
  407.  
  408.     /* This code requires that your xterm entry contain the kmous
  409.      * capability and that it be set to the \E[M documented in the
  410.      * Xterm Control Sequences reference.  This is how we
  411.      * arrange for mouse events to be reported via a KEY_MOUSE
  412.      * return value from wgetch().  After this value is received,
  413.      * _nc_mouse_inline() gets called and is immediately
  414.      * responsible for parsing the mouse status information
  415.      * following the prefix.
  416.      *
  417.      * The following quotes from the ctrlseqs.ms document in the
  418.      * X distribution, describing the X mouse tracking feature:
  419.      *
  420.      * Parameters for all mouse tracking escape sequences
  421.      * generated by xterm encode numeric parameters in a single
  422.      * character as value+040.  For example, !  is 1.
  423.      *
  424.      * On button press or release, xterm sends ESC [ M CbCxCy.
  425.      * The low two bits of Cb encode button information: 0=MB1
  426.      * pressed, 1=MB2 pressed, 2=MB3 pressed, 3=release.  The
  427.      * upper bits encode what modifiers were down when the
  428.      * button was pressed and are added together.  4=Shift,
  429.      * 8=Meta, 16=Control.  Cx and Cy are the x and y coordinates
  430.      * of the mouse event.  The upper left corner is (1,1).
  431.      *
  432.      * (End quote)  By the time we get here, we've eaten the
  433.      * key prefix.  FYI, the loop below is necessary because
  434.      * mouse click info isn't guaranteed to present as a
  435.      * single clist item.  It always does under Linux but often
  436.      * fails to under Solaris.
  437.      */
  438.     for (grabbed = 0; grabbed < 3; grabbed += res) {
  439.  
  440.         /* For VIO mouse we add extra bit 64 to disambiguate button-up. */
  441. #ifdef USE_EMX_MOUSE
  442.         res = read(M_FD(sp) >= 0 ? M_FD(sp) : sp->_ifd, &kbuf, 3);
  443. #else
  444.         res = read(sp->_ifd, kbuf + grabbed, 3 - grabbed);
  445. #endif
  446.         if (res == -1)
  447.         break;
  448.     }
  449.     kbuf[3] = '\0';
  450.  
  451.     TR(TRACE_IEVENT,
  452.        ("_nc_mouse_inline sees the following xterm data: '%s'", kbuf));
  453.  
  454.     eventp->id = 0;        /* there's only one mouse... */
  455.  
  456.     /* processing code goes here */
  457.     eventp->bstate = 0;
  458.     switch (kbuf[0] & 0x3) {
  459.     case 0x0:
  460.         eventp->bstate = BUTTON1_PRESSED;
  461. #ifdef USE_EMX_MOUSE
  462.         if (kbuf[0] & 0x40)
  463.         eventp->bstate = BUTTON1_RELEASED;
  464. #endif
  465.         break;
  466.  
  467.     case 0x1:
  468.         eventp->bstate = BUTTON2_PRESSED;
  469. #ifdef USE_EMX_MOUSE
  470.         if (kbuf[0] & 0x40)
  471.         eventp->bstate = BUTTON2_RELEASED;
  472. #endif
  473.         break;
  474.  
  475.     case 0x2:
  476.         eventp->bstate = BUTTON3_PRESSED;
  477. #ifdef USE_EMX_MOUSE
  478.         if (kbuf[0] & 0x40)
  479.         eventp->bstate = BUTTON3_RELEASED;
  480. #endif
  481.         break;
  482.  
  483.     case 0x3:
  484.         /*
  485.          * Release events aren't reported for individual buttons,
  486.          * just for the button set as a whole...
  487.          */
  488.         eventp->bstate =
  489.         (BUTTON1_RELEASED |
  490.          BUTTON2_RELEASED |
  491.          BUTTON3_RELEASED);
  492.         /*
  493.          * ...however, because there are no kinds of mouse events under
  494.          * xterm that can intervene between press and release, we can
  495.          * deduce which buttons were actually released by looking at the
  496.          * previous event.
  497.          */
  498.         prev = PREV(eventp);
  499.         if (!(prev->bstate & BUTTON1_PRESSED))
  500.         eventp->bstate &= ~BUTTON1_RELEASED;
  501.         if (!(prev->bstate & BUTTON2_PRESSED))
  502.         eventp->bstate &= ~BUTTON2_RELEASED;
  503.         if (!(prev->bstate & BUTTON3_PRESSED))
  504.         eventp->bstate &= ~BUTTON3_RELEASED;
  505.         break;
  506.     }
  507.  
  508.     if (kbuf[0] & 4) {
  509.         eventp->bstate |= BUTTON_SHIFT;
  510.     }
  511.     if (kbuf[0] & 8) {
  512.         eventp->bstate |= BUTTON_ALT;
  513.     }
  514.     if (kbuf[0] & 16) {
  515.         eventp->bstate |= BUTTON_CTRL;
  516.     }
  517.  
  518.     eventp->x = (kbuf[1] - ' ') - 1;
  519.     eventp->y = (kbuf[2] - ' ') - 1;
  520.     TR(MY_TRACE,
  521.        ("_nc_mouse_inline: primitive mouse-event %s has slot %ld",
  522.         _tracemouse(eventp),
  523.         (long) (eventp - events)));
  524.  
  525.     /* bump the next-free pointer into the circular list */
  526.     eventp = NEXT(eventp);
  527. #if 0                /* this return would be needed for QNX's mods to lib_getch.c */
  528.     return (TRUE);
  529. #endif
  530.     }
  531.  
  532.     return (FALSE);
  533. }
  534.  
  535. static void
  536. mouse_activate(bool on)
  537. {
  538.     if (!on && !initialized)
  539.     return;
  540.  
  541.     _nc_mouse_init();
  542.  
  543.     if (on) {
  544.  
  545.     switch (mousetype) {
  546.     case M_XTERM:
  547. #if NCURSES_EXT_FUNCS
  548.         keyok(KEY_MOUSE, on);
  549. #endif
  550.         TPUTS_TRACE("xterm mouse initialization");
  551. #ifdef USE_EMX_MOUSE
  552.         server_state(1);
  553. #else
  554.         putp("\033[?1000h");
  555. #endif
  556.         break;
  557. #if USE_GPM_SUPPORT
  558.     case M_GPM:
  559.         SP->_mouse_fd = gpm_fd;
  560.         break;
  561. #endif
  562.     }
  563.     /* Make runtime binding to cut down on object size of applications that
  564.      * do not use the mouse (e.g., 'clear').
  565.      */
  566.     SP->_mouse_event = _nc_mouse_event;
  567.     SP->_mouse_inline = _nc_mouse_inline;
  568.     SP->_mouse_parse = _nc_mouse_parse;
  569.     SP->_mouse_resume = _nc_mouse_resume;
  570.     SP->_mouse_wrap = _nc_mouse_wrap;
  571.  
  572.     } else {
  573.  
  574.     switch (mousetype) {
  575.     case M_XTERM:
  576.         TPUTS_TRACE("xterm mouse deinitialization");
  577. #ifdef USE_EMX_MOUSE
  578.         server_state(0);
  579. #else
  580.         putp("\033[?1000l");
  581. #endif
  582.         break;
  583. #if USE_GPM_SUPPORT
  584.     case M_GPM:
  585.         break;
  586. #endif
  587.     }
  588.     }
  589.     _nc_flush();
  590. }
  591.  
  592. /**************************************************************************
  593.  *
  594.  * Device-independent code
  595.  *
  596.  **************************************************************************/
  597.  
  598. static bool
  599. _nc_mouse_parse(int runcount)
  600. /* parse a run of atomic mouse events into a gesture */
  601. {
  602.     MEVENT *ep, *runp, *next, *prev = PREV(eventp);
  603.     int n;
  604.     bool merge;
  605.  
  606.     TR(MY_TRACE, ("_nc_mouse_parse(%d) called", runcount));
  607.  
  608.     /*
  609.      * When we enter this routine, the event list next-free pointer
  610.      * points just past a run of mouse events that we know were separated
  611.      * in time by less than the critical click interval. The job of this
  612.      * routine is to collaps this run into a single higher-level event
  613.      * or gesture.
  614.      *
  615.      * We accomplish this in two passes.  The first pass merges press/release
  616.      * pairs into click events.  The second merges runs of click events into
  617.      * double or triple-click events.
  618.      *
  619.      * It's possible that the run may not resolve to a single event (for
  620.      * example, if the user quadruple-clicks).  If so, leading events
  621.      * in the run are ignored.
  622.      *
  623.      * Note that this routine is independent of the format of the specific
  624.      * format of the pointing-device's reports.  We can use it to parse
  625.      * gestures on anything that reports press/release events on a per-
  626.      * button basis, as long as the device-dependent mouse code puts stuff
  627.      * on the queue in MEVENT format.
  628.      */
  629.     if (runcount == 1) {
  630.     TR(MY_TRACE,
  631.        ("_nc_mouse_parse: returning simple mouse event %s at slot %ld",
  632.         _tracemouse(prev),
  633.         (long) (prev - events)));
  634.     return (prev->id >= 0)
  635.         ? ((prev->bstate & eventmask) ? TRUE : FALSE)
  636.         : FALSE;
  637.     }
  638.  
  639.     /* find the start of the run */
  640.     runp = eventp;
  641.     for (n = runcount; n > 0; n--) {
  642.     runp = PREV(runp);
  643.     }
  644.  
  645. #ifdef TRACE
  646.     if (_nc_tracing & TRACE_IEVENT) {
  647.     _trace_slot("before mouse press/release merge:");
  648.     _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
  649.         (long) (runp - events),
  650.         (long) ((eventp - events) + (EV_MAX - 1)) % EV_MAX,
  651.         runcount);
  652.     }
  653. #endif /* TRACE */
  654.  
  655.     /* first pass; merge press/release pairs */
  656.     do {
  657.     merge = FALSE;
  658.     for (ep = runp; (next = NEXT(ep)) != eventp; ep = next) {
  659.         if (ep->x == next->x && ep->y == next->y
  660.         && (ep->bstate & (BUTTON1_PRESSED | BUTTON2_PRESSED | BUTTON3_PRESSED))
  661.         && (!(ep->bstate & BUTTON1_PRESSED)
  662.             == !(next->bstate & BUTTON1_RELEASED))
  663.         && (!(ep->bstate & BUTTON2_PRESSED)
  664.             == !(next->bstate & BUTTON2_RELEASED))
  665.         && (!(ep->bstate & BUTTON3_PRESSED)
  666.             == !(next->bstate & BUTTON3_RELEASED))
  667.         ) {
  668.         if ((eventmask & BUTTON1_CLICKED)
  669.             && (ep->bstate & BUTTON1_PRESSED)) {
  670.             ep->bstate &= ~BUTTON1_PRESSED;
  671.             ep->bstate |= BUTTON1_CLICKED;
  672.             merge = TRUE;
  673.         }
  674.         if ((eventmask & BUTTON2_CLICKED)
  675.             && (ep->bstate & BUTTON2_PRESSED)) {
  676.             ep->bstate &= ~BUTTON2_PRESSED;
  677.             ep->bstate |= BUTTON2_CLICKED;
  678.             merge = TRUE;
  679.         }
  680.         if ((eventmask & BUTTON3_CLICKED)
  681.             && (ep->bstate & BUTTON3_PRESSED)) {
  682.             ep->bstate &= ~BUTTON3_PRESSED;
  683.             ep->bstate |= BUTTON3_CLICKED;
  684.             merge = TRUE;
  685.         }
  686.         if (merge)
  687.             next->id = INVALID_EVENT;
  688.         }
  689.     }
  690.     } while
  691.     (merge);
  692.  
  693. #ifdef TRACE
  694.     if (_nc_tracing & TRACE_IEVENT) {
  695.     _trace_slot("before mouse click merge:");
  696.     _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
  697.         (long) (runp - events),
  698.         (long) ((eventp - events) + (EV_MAX - 1)) % EV_MAX,
  699.         runcount);
  700.     }
  701. #endif /* TRACE */
  702.  
  703.     /*
  704.      * Second pass; merge click runs.  At this point, click events are
  705.      * each followed by one invalid event. We merge click events
  706.      * forward in the queue.
  707.      *
  708.      * NOTE: There is a problem with this design!  If the application
  709.      * allows enough click events to pile up in the circular queue so
  710.      * they wrap around, it will cheerfully merge the newest forward
  711.      * into the oldest, creating a bogus doubleclick and confusing
  712.      * the queue-traversal logic rather badly.  Generally this won't
  713.      * happen, because calling getmouse() marks old events invalid and
  714.      * ineligible for merges.  The true solution to this problem would
  715.      * be to timestamp each MEVENT and perform the obvious sanity check,
  716.      * but the timer element would have to have sub-second resolution,
  717.      * which would get us into portability trouble.
  718.      */
  719.     do {
  720.     MEVENT *follower;
  721.  
  722.     merge = FALSE;
  723.     for (ep = runp; (next = NEXT(ep)) != eventp; ep = next)
  724.         if (ep->id != INVALID_EVENT) {
  725.         if (next->id != INVALID_EVENT)
  726.             continue;
  727.         follower = NEXT(next);
  728.         if (follower->id == INVALID_EVENT)
  729.             continue;
  730.  
  731.         /* merge click events forward */
  732.         if ((ep->bstate &
  733.              (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED))
  734.             && (follower->bstate &
  735.             (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED))) {
  736.             if ((eventmask & BUTTON1_DOUBLE_CLICKED)
  737.             && (follower->bstate & BUTTON1_CLICKED)) {
  738.             follower->bstate &= ~BUTTON1_CLICKED;
  739.             follower->bstate |= BUTTON1_DOUBLE_CLICKED;
  740.             merge = TRUE;
  741.             }
  742.             if ((eventmask & BUTTON2_DOUBLE_CLICKED)
  743.             && (follower->bstate & BUTTON2_CLICKED)) {
  744.             follower->bstate &= ~BUTTON2_CLICKED;
  745.             follower->bstate |= BUTTON2_DOUBLE_CLICKED;
  746.             merge = TRUE;
  747.             }
  748.             if ((eventmask & BUTTON3_DOUBLE_CLICKED)
  749.             && (follower->bstate & BUTTON3_CLICKED)) {
  750.             follower->bstate &= ~BUTTON3_CLICKED;
  751.             follower->bstate |= BUTTON3_DOUBLE_CLICKED;
  752.             merge = TRUE;
  753.             }
  754.             if (merge)
  755.             ep->id = INVALID_EVENT;
  756.         }
  757.  
  758.         /* merge double-click events forward */
  759.         if ((ep->bstate &
  760.              (BUTTON1_DOUBLE_CLICKED
  761.               | BUTTON2_DOUBLE_CLICKED
  762.               | BUTTON3_DOUBLE_CLICKED))
  763.             && (follower->bstate &
  764.             (BUTTON1_CLICKED | BUTTON2_CLICKED | BUTTON3_CLICKED))) {
  765.             if ((eventmask & BUTTON1_TRIPLE_CLICKED)
  766.             && (follower->bstate & BUTTON1_CLICKED)) {
  767.             follower->bstate &= ~BUTTON1_CLICKED;
  768.             follower->bstate |= BUTTON1_TRIPLE_CLICKED;
  769.             merge = TRUE;
  770.             }
  771.             if ((eventmask & BUTTON2_TRIPLE_CLICKED)
  772.             && (follower->bstate & BUTTON2_CLICKED)) {
  773.             follower->bstate &= ~BUTTON2_CLICKED;
  774.             follower->bstate |= BUTTON2_TRIPLE_CLICKED;
  775.             merge = TRUE;
  776.             }
  777.             if ((eventmask & BUTTON3_TRIPLE_CLICKED)
  778.             && (follower->bstate & BUTTON3_CLICKED)) {
  779.             follower->bstate &= ~BUTTON3_CLICKED;
  780.             follower->bstate |= BUTTON3_TRIPLE_CLICKED;
  781.             merge = TRUE;
  782.             }
  783.             if (merge)
  784.             ep->id = INVALID_EVENT;
  785.         }
  786.         }
  787.     } while
  788.     (merge);
  789.  
  790. #ifdef TRACE
  791.     if (_nc_tracing & TRACE_IEVENT) {
  792.     _trace_slot("before mouse event queue compaction:");
  793.     _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
  794.         (long) (runp - events),
  795.         (long) ((eventp - events) + (EV_MAX - 1)) % EV_MAX,
  796.         runcount);
  797.     }
  798. #endif /* TRACE */
  799.  
  800.     /*
  801.      * Now try to throw away trailing events flagged invalid, or that
  802.      * don't match the current event mask.
  803.      */
  804.     for (; runcount; prev = PREV(eventp), runcount--)
  805.     if (prev->id == INVALID_EVENT || !(prev->bstate & eventmask)) {
  806.         eventp = prev;
  807.     }
  808. #ifdef TRACE
  809.     if (_nc_tracing & TRACE_IEVENT) {
  810.     _trace_slot("after mouse event queue compaction:");
  811.     _tracef("_nc_mouse_parse: run starts at %ld, ends at %ld, count %d",
  812.         (long) (runp - events),
  813.         (long) ((eventp - events) + (EV_MAX - 1)) % EV_MAX,
  814.         runcount);
  815.     }
  816.     for (ep = runp; ep != eventp; ep = NEXT(ep))
  817.     if (ep->id != INVALID_EVENT)
  818.         TR(MY_TRACE,
  819.            ("_nc_mouse_parse: returning composite mouse event %s at slot %ld",
  820.         _tracemouse(ep),
  821.         (long) (ep - events)));
  822. #endif /* TRACE */
  823.  
  824.     /* after all this, do we have a valid event? */
  825.     return (PREV(eventp)->id != INVALID_EVENT);
  826. }
  827.  
  828. static void
  829. _nc_mouse_wrap(SCREEN * sp GCC_UNUSED)
  830. /* release mouse -- called by endwin() before shellout/exit */
  831. {
  832.     TR(MY_TRACE, ("_nc_mouse_wrap() called"));
  833.  
  834.     switch (mousetype) {
  835.     case M_XTERM:
  836.     if (eventmask)
  837.         mouse_activate(FALSE);
  838.     break;
  839. #if USE_GPM_SUPPORT
  840.     /* GPM: pass all mouse events to next client */
  841.     case M_GPM:
  842.     break;
  843. #endif
  844.     }
  845. }
  846.  
  847. static void
  848. _nc_mouse_resume(SCREEN * sp GCC_UNUSED)
  849. /* re-connect to mouse -- called by doupdate() after shellout */
  850. {
  851.     TR(MY_TRACE, ("_nc_mouse_resume() called"));
  852.  
  853.     /* xterm: re-enable reporting */
  854.     if (mousetype == M_XTERM && eventmask)
  855.     mouse_activate(TRUE);
  856.  
  857.     /* GPM: reclaim our event set */
  858. }
  859.  
  860. /**************************************************************************
  861.  *
  862.  * Mouse interface entry points for the API
  863.  *
  864.  **************************************************************************/
  865.  
  866. NCURSES_EXPORT(int)
  867. getmouse(MEVENT * aevent)
  868. /* grab a copy of the current mouse event */
  869. {
  870.     T((T_CALLED("getmouse(%p)"), aevent));
  871.  
  872.     if (aevent && (mousetype != M_NONE)) {
  873.     /* compute the current-event pointer */
  874.     MEVENT *prev = PREV(eventp);
  875.  
  876.     /* copy the event we find there */
  877.     *aevent = *prev;
  878.  
  879.     TR(TRACE_IEVENT, ("getmouse: returning event %s from slot %ld",
  880.               _tracemouse(prev),
  881.               (long) (prev - events)));
  882.  
  883.     prev->id = INVALID_EVENT;    /* so the queue slot becomes free */
  884.     returnCode(OK);
  885.     }
  886.     returnCode(ERR);
  887. }
  888.  
  889. NCURSES_EXPORT(int)
  890. ungetmouse(MEVENT * aevent)
  891. /* enqueue a synthesized mouse event to be seen by the next wgetch() */
  892. {
  893.     T((T_CALLED("ungetmouse(%p)"), aevent));
  894.  
  895.     /* stick the given event in the next-free slot */
  896.     *eventp = *aevent;
  897.  
  898.     /* bump the next-free pointer into the circular list */
  899.     eventp = NEXT(eventp);
  900.  
  901.     /* push back the notification event on the keyboard queue */
  902.     returnCode(ungetch(KEY_MOUSE));
  903. }
  904.  
  905. NCURSES_EXPORT(mmask_t)
  906. mousemask(mmask_t newmask, mmask_t * oldmask)
  907. /* set the mouse event mask */
  908. {
  909.     mmask_t result = 0;
  910.  
  911.     T((T_CALLED("mousemask(%#lx,%p)"), newmask, oldmask));
  912.  
  913.     if (oldmask)
  914.     *oldmask = eventmask;
  915.  
  916.     if (!newmask && !initialized)
  917.     returnBits(0);
  918.  
  919.     _nc_mouse_init();
  920.     if (mousetype != M_NONE) {
  921.     eventmask = newmask &
  922.         (BUTTON_ALT | BUTTON_CTRL | BUTTON_SHIFT
  923.          | BUTTON1_PRESSED | BUTTON1_RELEASED | BUTTON1_CLICKED
  924.          | BUTTON1_DOUBLE_CLICKED | BUTTON1_TRIPLE_CLICKED
  925.          | BUTTON2_PRESSED | BUTTON2_RELEASED | BUTTON2_CLICKED
  926.          | BUTTON2_DOUBLE_CLICKED | BUTTON2_TRIPLE_CLICKED
  927.          | BUTTON3_PRESSED | BUTTON3_RELEASED | BUTTON3_CLICKED
  928.          | BUTTON3_DOUBLE_CLICKED | BUTTON3_TRIPLE_CLICKED);
  929.  
  930.     mouse_activate(eventmask != 0);
  931.  
  932.     result = eventmask;
  933.     }
  934.  
  935.     returnBits(result);
  936. }
  937.  
  938. NCURSES_EXPORT(bool)
  939. wenclose(const WINDOW *win, int y, int x)
  940. /* check to see if given window encloses given screen location */
  941. {
  942.     bool result = FALSE;
  943.  
  944.     T((T_CALLED("wenclose(%p,%d,%d)"), win, y, x));
  945.  
  946.     if (win != 0) {
  947.     y -= win->_yoffset;
  948.     result = ((win->_begy <= y &&
  949.            win->_begx <= x &&
  950.            (win->_begx + win->_maxx) >= x &&
  951.            (win->_begy + win->_maxy) >= y) ? TRUE : FALSE);
  952.     }
  953.     returnBool(result);
  954. }
  955.  
  956. NCURSES_EXPORT(int)
  957. mouseinterval(int maxclick)
  958. /* set the maximum mouse interval within which to recognize a click */
  959. {
  960.     int oldval;
  961.  
  962.     T((T_CALLED("mouseinterval(%d)"), maxclick));
  963.  
  964.     if (SP != 0) {
  965.     oldval = SP->_maxclick;
  966.     if (maxclick >= 0)
  967.         SP->_maxclick = maxclick;
  968.     } else {
  969.     oldval = DEFAULT_MAXCLICK;
  970.     }
  971.  
  972.     returnCode(oldval);
  973. }
  974.  
  975. /* This may be used by other routines to ask for the existence of mouse
  976.    support */
  977. NCURSES_EXPORT(int)
  978. _nc_has_mouse(void)
  979. {
  980.     return (mousetype == M_NONE ? 0 : 1);
  981. }
  982.  
  983. NCURSES_EXPORT(bool)
  984. wmouse_trafo(const WINDOW *win, int *pY, int *pX, bool to_screen)
  985. {
  986.     bool result = FALSE;
  987.  
  988.     T((T_CALLED("wmouse_trafo(%p,%p,%p,%d)"), win, pY, pX, to_screen));
  989.  
  990.     if (win && pY && pX) {
  991.     int y = *pY;
  992.     int x = *pX;
  993.  
  994.     if (to_screen) {
  995.         y += win->_begy + win->_yoffset;
  996.         x += win->_begx;
  997.         if (wenclose(win, y, x))
  998.         result = TRUE;
  999.     } else {
  1000.         if (wenclose(win, y, x)) {
  1001.         y -= (win->_begy + win->_yoffset);
  1002.         x -= win->_begx;
  1003.         result = TRUE;
  1004.         }
  1005.     }
  1006.     if (result) {
  1007.         *pX = x;
  1008.         *pY = y;
  1009.     }
  1010.     }
  1011.     returnBool(result);
  1012. }
  1013.  
  1014. /* lib_mouse.c ends here */
  1015.